package com.zhala.web.config;

import com.sun.jna.Memory;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

import com.zhala.common.core.domain.CommonResult;
import com.zhala.system.domain.ZlAibox;
import com.zhala.system.domain.ZlScada;
import com.zhala.system.service.ZlAiboxService;
import com.zhala.system.service.ZlScadaService;
import com.zhala.web.ai.lib.NetSDKLib;
import com.zhala.web.ai.lib.ToolKits;
import com.zhala.web.ai.lib.Utils;
import com.zhala.web.ai.lib.structure.NET_ATTRIBUTE_INFO;
import com.zhala.web.ai.lib.structure.NET_GET_CONDITION_INFO;
import com.zhala.web.ai.lib.structure.NET_IN_SCADA_GET_ATTRIBUTE_INFO;
import com.zhala.web.ai.lib.structure.NET_OUT_SCADA_GET_ATTRIBUTE_INFO;
import com.zhala.web.ai.module.LoginModule;
import com.zhala.web.pasystem.AlarmService;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Vector;



@Component
public class HumanNumberStatisticConfig implements ApplicationListener<ApplicationReadyEvent> {

    private static ZlScadaService service;

    private static ZlAiboxService zlAiboxService;

    private static AlarmService alarmService;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        System.out.print("环境监测");
        service = event.getApplicationContext().getBean(ZlScadaService.class);
        zlAiboxService = event.getApplicationContext().getBean(ZlAiboxService.class);
        alarmService = event.getApplicationContext().getBean(AlarmService.class);
        start();
    }

    private void start(){
        //初始化
//        LoginModule.init(DefaultDisConnect.GetInstance(), DefaultHaveReconnect.getINSTANCE());
        //登录
        login("192.168.1.108",37777,"admin","ggc123456");//todo 改为真实的
        //获取设备
        getDeviceIdList();
        //获取设备点位
        getDeviceAttribute();
        //订阅监控点报警
        scadaAlarmAttachInfo();
        //订阅监控点实时信息
        scadaAttachInfo();
        //订阅报警信息
        startListenEx();
    }

    public static NetSDKLib.LLong this_m_hLoginHandle = new NetSDKLib.LLong(0);
    /**
     * 登录
     */
    public boolean login(String ip,Integer port,String suer,String pwd) {
        System.out.println(ip);
        System.out.println(port);
        System.out.println(suer);
        System.out.println(pwd);
        this_m_hLoginHandle =  LoginModule.login2(ip,port,suer,pwd);
        boolean a =  this_m_hLoginHandle.longValue() == 0? false:true;
        if (a) {
            System.out.println("环境检测登陆成功");
            return true;
        } else {
            System.out.println("登陆失败");
            return false;
        }
    }


    /**
     * 获取当前主机接入的外部设备ID
     */
    Vector<String> deviceIds = new Vector<>();
    // 多平台 编码
    private final static Charset encode = Charset.forName(Utils.getPlatformEncode());

    public boolean getDeviceIdList() {
        deviceIds.clear();

        // model.setColumnCount(0);
        int nCount = 64; //

        NetSDKLib.NET_SCADA_DEVICE_ID_INFO[] stuDeviceIDList = new NetSDKLib.NET_SCADA_DEVICE_ID_INFO[nCount];
        for (int i = 0; i < stuDeviceIDList.length; ++i) {
            stuDeviceIDList[i] = new NetSDKLib.NET_SCADA_DEVICE_ID_INFO();
        }

        NetSDKLib.NET_SCADA_DEVICE_LIST stuSCADADeviceInfo = new NetSDKLib.NET_SCADA_DEVICE_LIST();
        stuSCADADeviceInfo.nMax = nCount;
        int nSize = stuDeviceIDList[0].size() * nCount;
        stuSCADADeviceInfo.pstuDeviceIDInfo = new Memory(nSize);   // 监测设备信息
        stuSCADADeviceInfo.pstuDeviceIDInfo.clear(nSize);
        ToolKits.SetStructArrToPointerData(stuDeviceIDList, stuSCADADeviceInfo.pstuDeviceIDInfo);
        if (queryDevState(NetSDKLib.NET_DEVSTATE_SCADA_DEVICE_LIST, stuSCADADeviceInfo)) {
            System.out.println("获取设备状态异常");
        }

        if (stuSCADADeviceInfo.nRet == 0) {
            System.out.println("当前主机接入的外部设备ID有效个数为0.");                        // 外部设备没有有效ID
            return false;
        }

        // 从 Pointer 提取数据
        ToolKits.GetPointerDataToStructArr(stuSCADADeviceInfo.pstuDeviceIDInfo, stuDeviceIDList);
        // 打印数据并更新设备ID
        System.out.println("获取当前主机接入的外部设备ID的有效个数：" + stuSCADADeviceInfo.nRet);
        if(Objects.nonNull(stuSCADADeviceInfo)){
            for (int i = 0; i < stuSCADADeviceInfo.nRet; ++i) {
                String deviceID = "";
                String deviceName = "";

                deviceName = new String(stuDeviceIDList[i].szDevName, encode).trim();
                deviceID = new String(stuDeviceIDList[i].szDeviceID, encode).trim();
                System.out.printf("外部设备[%d] 设备id[%s] 设备名称[%s]\n", i,
                        new String(stuDeviceIDList[i].szDeviceID, encode).trim(), deviceName);
                deviceIds.add(deviceID);
            }
        }


        return true;
    }

    /**
     * 查询设备状态
     */
    public boolean queryDevState(int nType, NetSDKLib.SdkStructure stuInfo) {

        IntByReference intRetLen = new IntByReference();
        stuInfo.write();
        if (!LoginModule.netsdk.CLIENT_QueryDevState(this_m_hLoginHandle, nType, stuInfo.getPointer(), stuInfo.size(), intRetLen, 3000)) {
            return true;
        }
        stuInfo.read();
        return false;
    }


    /**
     * 获取设备点位信息
     */
    public void getDeviceAttribute() {
        Map<Integer, String> num2PointType = new HashMap<Integer, String>(6);
        num2PointType.put(0, "UNKNOWN");
        num2PointType.put(1, "ALL");
        num2PointType.put(2, "YC");
        num2PointType.put(3, "YX");
        num2PointType.put(4, "YT");
        num2PointType.put(5, "YK");
        for (int i = 0; i < deviceIds.size(); i++) {
            String deviceId = deviceIds.get(i);
            int nMaxAttributeInfoNum = 20;
            NET_IN_SCADA_GET_ATTRIBUTE_INFO inParam = new NET_IN_SCADA_GET_ATTRIBUTE_INFO(); // 获取设备点位信息 入参
            NET_OUT_SCADA_GET_ATTRIBUTE_INFO outParam = new NET_OUT_SCADA_GET_ATTRIBUTE_INFO(); // 获取设备点位信息 出参
            NET_GET_CONDITION_INFO conditionInfo = new NET_GET_CONDITION_INFO(); // 获取条件
            conditionInfo.szDeviceID = deviceIds.get(i).getBytes();
            conditionInfo.bIsSendID = 1;
            outParam.nMaxAttributeInfoNum = nMaxAttributeInfoNum;
            NET_ATTRIBUTE_INFO attributeInfo = new NET_ATTRIBUTE_INFO(); // 设备点位信息(内存由用户申请)

            outParam.pstuAttributeInfo = new Memory(nMaxAttributeInfoNum * attributeInfo.size());
            inParam.stuCondition = conditionInfo;
            inParam.write();
            outParam.write();
            boolean ret = LoginModule.netsdk.CLIENT_SCADAGetAttributeInfo(this_m_hLoginHandle, inParam.getPointer(), outParam.getPointer(), 3000);
            inParam.read();
            outParam.read();

            if (ret) {
                System.out.println("SCADAGetAttributeInfo succeed!");
                outParam.read();
                int retAttributeInfoNum = outParam.nRetAttributeInfoNum;
                System.out.println(outParam);

                NET_ATTRIBUTE_INFO infos[] = new NET_ATTRIBUTE_INFO[retAttributeInfoNum];
                for (int j = 0; j < retAttributeInfoNum; j++) {
                    infos[j] = new NET_ATTRIBUTE_INFO();
                }
                System.err.println("infos 大小： " + infos.length);
                ToolKits.GetPointerDataToStructArr(outParam.pstuAttributeInfo, infos);

                for (int n = 0; n < retAttributeInfoNum; n++) {
                    NET_ATTRIBUTE_INFO out = new NET_ATTRIBUTE_INFO();
                    out = infos[n];

                    System.out.println("设备ID：" + deviceId
                            + "\n" + "点位类型: " + num2PointType.get(out.emPointType)
                            + "\n" + "点位名称: " + new String(out.szSignalName, encode).trim()
                            + "\n" + "时延: " + out.nDelay);

                }
            } else {
                System.out.println("获取设备点位信息异常");
            }
        }
    }


    private NetSDKLib.LLong attachInfoHandle = new NetSDKLib.LLong(0);  // 监测点位信息，订阅句柄
    private NetSDKLib.LLong alarmAttachInfoHandle = new NetSDKLib.LLong(0);  // 监测点位报警，订阅句柄
    /**
     * 订阅监测点位报警
     */
    public void scadaAlarmAttachInfo() {
        // 入参
        NetSDKLib.NET_IN_SCADA_ALARM_ATTACH_INFO stIn = new NetSDKLib.NET_IN_SCADA_ALARM_ATTACH_INFO();
        stIn.cbCallBack = SCADAAlarmAttachInfoCallBack.getInstance();
        // 出参
        NetSDKLib.NET_OUT_SCADA_ALARM_ATTACH_INFO stOut = new NetSDKLib.NET_OUT_SCADA_ALARM_ATTACH_INFO();
        //todo m_hLoginHandle这个值是登录后的ID，不同ip登陆的话，要把这个值做隔离
        alarmAttachInfoHandle = LoginModule.netsdk.CLIENT_SCADAAlarmAttachInfo(this_m_hLoginHandle, stIn, stOut, 3000);
    }

    /**
     * 取消订阅监测点位报警信息
     */
    public void scadaAlarmDetachInfo() {
        if (alarmAttachInfoHandle.longValue() != 0) {
            LoginModule.netsdk.CLIENT_SCADAAlarmDetachInfo(alarmAttachInfoHandle);
            alarmAttachInfoHandle.setValue(0);
            System.out.println("CLIENT_SCADADetachInfo: 取消订阅监测点位实时信息成功！");
        }
    }

    /**
     * 订阅监测点位报警回调
     */
    public static class SCADAAlarmAttachInfoCallBack implements NetSDKLib.fSCADAAlarmAttachInfoCallBack {
        private static SCADAAlarmAttachInfoCallBack INSTANCE;
        private SCADAAlarmAttachInfoCallBack() {
        }
        public static SCADAAlarmAttachInfoCallBack getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new SCADAAlarmAttachInfoCallBack();
            }
            return INSTANCE;
        }
        @Override
        public void invoke(NetSDKLib.LLong lAttachHandle,
                           NetSDKLib.NET_SCADA_NOTIFY_POINT_ALARM_INFO_LIST pInfo, int nBufLen,
                           Pointer dwUser) {

            System.out.println("————————————————————【订阅监测点报警信息回调】————————————————————");
            //更新列表
            for (int i = 0; i < pInfo.nList; i++) {
                System.out.println(" 设备ID:" + new String(pInfo.stuList[i].szDevID).trim());
                System.out.println(" 点位ID:" + new String(pInfo.stuList[i].szPointID).trim());
                String alarmDesc = new String(pInfo.stuList[i].szAlarmDesc, encode).trim();
                System.out.println(" 报警描述:" + alarmDesc);

                System.out.println(" 报警标志:" + (pInfo.stuList[i].bAlarmFlag == 1));
                System.out.println(" 报警时间:" + pInfo.stuList[i].stuAlarmTime.toStringTime());
                System.out.println(" 报警级别(0~6):" + pInfo.stuList[i].nAlarmLevel);
                System.out.println(" 报警编号(同一个告警的开始和结束的编号是相同的):" + pInfo.stuList[i].nSerialNo);
            }
            System.out.println("————————————————————【订阅监测点报警信息回调】————————————————————");
        }


    }



    /**
     * 订阅监测点位实时信息
     */
    public void scadaAttachInfo() {
        // 入参
        NetSDKLib.NET_IN_SCADA_ATTACH_INFO stIn = new NetSDKLib.NET_IN_SCADA_ATTACH_INFO();
        stIn.cbCallBack = SCADAAttachInfoCallBack.getInstance();
        // 出参
        System.err.println("登录句柄： " + this_m_hLoginHandle);
        NetSDKLib.NET_OUT_SCADA_ATTACH_INFO stOut = new NetSDKLib.NET_OUT_SCADA_ATTACH_INFO();
        attachInfoHandle = LoginModule.netsdk.CLIENT_SCADAAttachInfo(this_m_hLoginHandle, stIn, stOut, 3000);
        if (attachInfoHandle.longValue() != 0) {
            System.out.println("CLIENT_SCADAAttachInfo: 订阅监测点位实时信息成功！");
        } else {
            System.out.println("CLIENT_SCADAAttachInfo: 订阅监测点位实时信息失败！");
        }
    }

    /**
     * 取消订阅监测点位实时信息
     */
    public void scadaDetachInfo() {
        if (attachInfoHandle.longValue() != 0) {
            LoginModule.netsdk.CLIENT_SCADADetachInfo(attachInfoHandle);
            System.out.println("CLIENT_SCADADetachInfo: 取消订阅监测点位实时信息成功！");
        }
    }


    /**
     * 订阅监测点位信息回调
     */
    private static class SCADAAttachInfoCallBack implements NetSDKLib.fSCADAAttachInfoCallBack {
        private static SCADAAttachInfoCallBack INSTANCE;

        private SCADAAttachInfoCallBack(){
        }

        public static final SCADAAttachInfoCallBack getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new SCADAAttachInfoCallBack();
            }

            return INSTANCE;
        }

        @Override
        public void invoke(NetSDKLib.LLong lLoginID, NetSDKLib.LLong lAttachHandle,
                           NetSDKLib.NET_SCADA_NOTIFY_POINT_INFO_LIST pInfo, int nBufLen, Pointer dwUser) {
            System.out.println("————————————————————【订阅监测点位信息回调】————————————————————");

            ZlScada zlScada = new ZlScada();
            //更新列表
            int a = 1;
            for (int i = 0; i < pInfo.nList; i++) {
                String id = new String(pInfo.stuList[i].szID).trim();
                switch (id){
                    case "18104001"://风速
//                        System.out.println("风速"+pInfo.stuList[i].fValue);
                        Double wind =  new Double(pInfo.stuList[i].fValue);
                        zlScada.setWind(wind);
                        a = service.saveOrUpdate(zlScada);

                        if(wind >= 11){
                            ZlAibox zlAibox = new ZlAibox();
                            zlAibox.setCamera_name("0"); // 确保ZlAibox类有这个方法
                            zlAibox.setType("STRONG_WIND"); // 确保ZlAibox类有这个方法
                            alarmService.addAlarmTask("STRONG_WIND");//写入报警信息
                            // 保存zlAibox对象并返回结果
                            CommonResult.success(zlAiboxService.save(zlAibox));
                        }
                        break;
                    case "18105001"://风向
//                        System.out.println("风向"+pInfo.stuList[i].fValue);
                        zlScada.setAir(new Double(pInfo.stuList[i].fValue));
                        a = service.saveOrUpdate(zlScada);
                        break;
                    case "18113001"://PM2.5
//                        System.out.println("PM2.5"+pInfo.stuList[i].fValue);
                        zlScada.setPm25(new Double(pInfo.stuList[i].fValue));
                        a = service.saveOrUpdate(zlScada);
                        break;
                    case "18114001"://PM10
//                        System.out.println("PM10"+pInfo.stuList[i].fValue);
                        zlScada.setPm10(new Double(pInfo.stuList[i].fValue));
                        a = service.saveOrUpdate(zlScada);
                        break;
                    case "18118001"://温度
//                        System.out.println("温度"+pInfo.stuList[i].fValue);
                        zlScada.setTemperature(new Double(pInfo.stuList[i].fValue));
                        a = service.saveOrUpdate(zlScada);
                        break;
                    case "18119001"://湿度
//                        System.out.println("湿度"+pInfo.stuList[i].fValue);
                        zlScada.setHumidity(new Double(pInfo.stuList[i].fValue));
                        a = service.saveOrUpdate(zlScada);
                        break;
                    case "18120001"://噪音
//                        System.out.println("噪音"+pInfo.stuList[i].fValue);
                        zlScada.setNoise(new Double(pInfo.stuList[i].fValue));
                        a = service.saveOrUpdate(zlScada);
                        break;
                    case "18121001"://气压
//                        System.out.println("气压"+pInfo.stuList[i].fValue);
                        zlScada.setDirection(new Double(pInfo.stuList[i].fValue));
                        a = service.saveOrUpdate(zlScada);
                        break;
                }
            }

//            System.out.println("————————————————————【订阅监测点位信息回调】————————————————————");
        }
    }



    /**
     * 订阅报警信息
     */
    public void startListenEx() {
        LoginModule.netsdk.CLIENT_SetDVRMessCallBack(MessCallback.getInstance(), null); // set alarm listen callback
        boolean b = LoginModule.netsdk.CLIENT_StartListenEx(this_m_hLoginHandle);
        if (b) {
            System.out.println("CLIENT_StartListenEx success.");
        }
    }


    /**
     * 取消报警信息
     */
    public void stopListen() {
        LoginModule.netsdk.CLIENT_StopListen(this_m_hLoginHandle);
    }

    /**
     * 普通报警监听回调
     */
    private static class MessCallback implements NetSDKLib.fMessCallBack {
        private static MessCallback INSTANCE;

        private MessCallback() {

        }

        public static MessCallback getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new MessCallback();
            }
            return INSTANCE;
        }

        @Override
        public boolean invoke(int lCommand, NetSDKLib.LLong lLoginID, Pointer pStuEvent,
                              int dwBufLen, String strDeviceIP, NativeLong nDevicePort,
                              Pointer dwUser) {
            switch (lCommand) {

                case NetSDKLib.NET_ALARM_SCADA_DEV_ALARM: {  // 12706 检测采集设备报警事件   "SCADADevAlarm"

                    NetSDKLib.ALARM_SCADA_DEV_INFO msg = new NetSDKLib.ALARM_SCADA_DEV_INFO();
                    ToolKits.GetPointerData(pStuEvent, msg);
                    String deviceId = new String(msg.szDevID,encode).trim();
                    System.out.println("[检测采集设备报警事件] nChannel :" + msg.nChannel);
                    System.out.println("设备ID :" + deviceId);
                    String description = new String(msg.szDesc, encode).trim();
                    String deviceName = new String(msg.szDevName,encode).trim();
                    System.out.println( " nAction :" + msg.nAction +
                            " nAlarmFlag :" + msg.nAlarmFlag + "\n" + "故障设备名称" + deviceName
                            + "\n" + "描述" + description + "时间" + msg.stuTime);
//                        NetSDKLib.ALARM_SCADA_DEV_INFO msg = new NetSDKLib.ALARM_SCADA_DEV_INFO();
//                        ToolKits.GetPointerData(pStuEvent, msg);
                    break;
                }
                default:
                    System.out.println("What's lCommand: " + lCommand);
                    break;

            }
            return true;
        }
    }

}
